#include "../include/memory.h"

extern qword iregs[128];
extern real4 fregs[32];
extern real8 dregs[32];
extern VREG vregs[64];

std::fstream disk;
Table gdt[4096];
byte ram[MAX_MEM];

const char init_command[8] = {'\x32', '\x00', '\x7f', '\x07', '\x00', '\x00', '\x02', '\x00'};

void init()
{
	set_valueI(DMA_ON + IT_ON + RUNNING, Flag);
	set_valueI(0xffff, Base);
	set_valueI(0xffff, Stack);
	memcpy(ram, init_command, sizeof(qword));
}
qword AddrTrans(qword v_addr)
{
	if (iregs[Flag] & (PROTECTED + DMA_ON))
		return v_addr & 0xffff;
	qword addr = v_addr & 0xffffffffffff;
	word sector = (word)(v_addr >> 48);
	bool isSys = sector >> 15;
	byte operation = (byte)(sector >> 12) & 0x07;
	sector = sector & 0xfff;
	if (sector >= iregs[GDT_Count])
	{
		// TODO:Throw Error:Invalid GDT
	}
	qword mBase = gdt[sector].base;
	mBase <<= 16;
	qword mLimit = mBase + (gdt[sector].descript & 0x10) ? (qword)(gdt[sector].length) << 16 : (qword)(gdt[sector].length) << 32;
	if ((addr >= mBase) || (addr < mLimit))
	{
		// TODO:Throw Error:Address out of range
	}
	if ((operation == 0) || (operation == 4) || (operation == 6))
	{
		// TODO:Throw Error:Invalid operation code
	}
	switch (gdt[sector].descript & 0x07)
	{
	case RO:
		if (operation != RO)
		{
			// TODO:Throw Error:Operation not permitted
		}
		break;
	case WO:
		if (operation != WO)
		{
			// TODO:Throw Error:Operation not permitted
		}
		break;
	case RW:
		if (operation > RW)
		{
			// TODO:Throw Error:Operation not permitted
		}
		break;
	case RX:
		if (operation > RX)
		{
			// TODO:Throw Error:Operation not permitted
		}
		break;
	}
	if (isSys == true)
	{
		if (!(gdt[sector].descript & 0x01))
		{
			// TODO:Throw Error:Access Denied
		}
	}
	return addr + mBase;
}
template <class T>
void putMem(qword v_addr, T data)
{
	memcpy(ram + AddrTrans(v_addr), &data, sizeof(T));
}
template <class T>
T getMem(qword v_addr)
{
	T result;
	memcpy(&result, ram + AddrTrans(v_addr), sizeof(T));
	return result;
}
void input(qword address)
{
	*(ram + address) = getchar();
}
void output(qword address)
{
	putchar(*(ram + address));
}
void write_gdt(word target, qword address)
{
	memcpy(gdt + target, ram + AddrTrans(address), sizeof(Table));
}
void mount()
{
	disk.open("dev_sda.bin", std::ios::binary);
}
void umount()
{
	disk.close();
}
void file_read()
{
	if (!disk.is_open())
	{
		// TODO:Throw Error:Disk unmounted
	}
	qword mem = AddrTrans(iregs[DI]);
	disk.seekg(iregs[SI]);
	disk.read((char *)(ram + mem), 512 * iregs[Count]);
}
void file_write()
{
	if (!disk.is_open())
	{
		// TODO:Throw Error:Disk unmounted
	}
	qword mem = AddrTrans(iregs[SI]);
	disk.seekp(iregs[DI]);
	disk.write((char *)(ram + mem), 512 * iregs[Count]);
}